﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Threading;

namespace Servidor
{
    public class Controlador
    {
        private static Controlador _instancia = null;

        private ProcesosSincronizacionServidores proccesSinocronizarServers;
        private ProcesosAtencionClientes proccesAtenderClients;
        private Thread threadDescubrirServers;
        private Thread threadAtencionServers;
        private Thread threadEnvioDatosServer;
        private ManualResetEvent[] resetEventsAtenderClientes; 
        private ManualResetEvent[] resetEventsAtenderClientesServer;
        private TcpListener[] inputArrayAtenderClientes;
        private TcpListener[] inputArrayAtenderClientesServer;

        public static Controlador Instancia 
        {
            get
            {
                if (_instancia == null)
                {
                    _instancia = new Controlador();
                }
                return _instancia;
            }
        }

        protected Controlador()
        {
        }

        public void CerrarConexion()
        {
            proccesSinocronizarServers.RequestStop();
            threadEnvioDatosServer.Abort();
            threadAtencionServers.Abort();
            proccesAtenderClients.RequestStop();
            threadDescubrirServers.Abort();
        }
       
        // Comienza los procesos
        public void IniciarProcesos()
        {
            Utilities.LogMessage("Iniciando Servidor");

            // SECTOR ATENCION DE CLIENTES
            proccesAtenderClients = new ProcesosAtencionClientes();

            //Proceso atender clientes TCP
            Utilities.LogMessage(string.Format("Iniciando {0} procesos de atencion de clientes", DatosServidor.Instancia.MaxConexionesClientes));
            resetEventsAtenderClientes = new ManualResetEvent[DatosServidor.Instancia.MaxConexionesClientes];
            inputArrayAtenderClientes = new TcpListener[DatosServidor.Instancia.MaxConexionesClientes];
            
            IPEndPoint endpoint = new IPEndPoint(IPAddress.Any, DatosServidor.Instancia.PuertoTCPClientes);
            var tcpListener = new TcpListener(endpoint);

            for (int i = 0; i < DatosServidor.Instancia.MaxConexionesClientes; i++)
            {
                resetEventsAtenderClientes[i] = new ManualResetEvent(false);
                inputArrayAtenderClientes[i] = tcpListener;
                ThreadPool.QueueUserWorkItem(new WaitCallback(DoWork), i);
            }

            //Proceso descubrimiento clientes
            Utilities.LogMessage("Iniciando procesos de atencion de descubrimiento de clientes");
            ThreadStart starterAtenderDescubrimiento = delegate { proccesAtenderClients.AtenderDescubrimientoClientes(); };
            threadDescubrirServers = new Thread(starterAtenderDescubrimiento);
            threadDescubrirServers.IsBackground = true;
            threadDescubrirServers.Start();

            // SECTOR SERVIDORES - Atencion y sincronizacion de servidores
            proccesSinocronizarServers = new ProcesosSincronizacionServidores();

            // Proceso que envia la informacion del servidor a los otros servidores
            Utilities.LogMessage("Iniciando proceso de envio de datos a servidores");
            ThreadStart starterSendDataServer = delegate { proccesSinocronizarServers.EnviarDatosServidor(); };
            threadEnvioDatosServer = new Thread(starterSendDataServer);
            threadEnvioDatosServer.IsBackground = true;
            threadEnvioDatosServer.Start();

            // Proceso de atencion de servidores
            Utilities.LogMessage("Iniciando proceso de atencion de sincronizacion de servidores");
            ThreadStart starterSincronizacionServidores = delegate {proccesSinocronizarServers.AtenderSincronizacionServidores(); };
            threadAtencionServers = new Thread(starterSincronizacionServidores);
            threadAtencionServers.IsBackground = true;
            threadAtencionServers.Start();
            
            //Proceso de envio de archivos para sincronizar servidores
            Utilities.LogMessage(string.Format("Iniciando {0} procesos de envio de archivos para sincronizar servidores", DatosServidor.Instancia.CantidadMaximaConexionesSincronizacion));
            resetEventsAtenderClientesServer = new ManualResetEvent[DatosServidor.Instancia.CantidadMaximaConexionesSincronizacion];
            inputArrayAtenderClientesServer = new TcpListener[DatosServidor.Instancia.CantidadMaximaConexionesSincronizacion];
            IPEndPoint endpointServs = new IPEndPoint(IPAddress.Any, DatosServidor.Instancia.PuertoTCPSincronizarServidores);
            var tcpListenerServs = new TcpListener(endpointServs);
            for (int i = 0; i < DatosServidor.Instancia.CantidadMaximaConexionesSincronizacion; i++)
            {
                resetEventsAtenderClientesServer[i] = new ManualResetEvent(false);
                inputArrayAtenderClientesServer[i] = tcpListenerServs;
                ThreadPool.QueueUserWorkItem(new WaitCallback(DoWorkServer), i);
            }
        }

        private void DoWork(object o)
        {
            int index = (int)o;
            try
            {

                this.proccesAtenderClients.AtenderClientesTCP(inputArrayAtenderClientes[index]);
            }
            catch (ServerException ex)
            {
                resetEventsAtenderClientes[index].Set();
                Utilities.LogMessage(string.Format("Fin proceso de Atencion de clientes"));
            }
        }

        private void DoWorkServer(object o)
        {
            int index = (int)o;
            try
            {

                this.proccesAtenderClients.AtenderClientesTCP(inputArrayAtenderClientesServer[index]);
            }
            catch (ServerException ex)
            {
                resetEventsAtenderClientesServer[index].Set();
                Utilities.LogMessage(string.Format("Fin proceso de Atencion de clientes"));
            }
        }

    }
}
